
#include "../crypt/RSAKeyPair.h"
#include "../iface/CryptInterface.h"
#include "../net/netgeneral.h"
#include "../utils/BaseConvert.h"
#include "../utils/StringUtils.h"
#include "../net/Broker4.h"
#include "../iface/APRInterface.h"
#include "../xml/XMLDescriptor.h"
#include "BrokerMessage.h"

void BrokerMessage::TakeAndInsertPublicKeyByName(KeyDB *kdb, int payload_number, const char *iformat, ...)
{
   if (!kdb)
   {
      errprint("key database is null");
      return;
   }
   int total_payloads = GetHowManyPayloads();
   if ((payload_number < 0) || (payload_number >= total_payloads))
   {
      errprint("payload number out of range");
      return;
   }
   va_list args; va_list newcopy;
   va_start(args, iformat); va_copy(newcopy, args);
   const char *kname = StringUtils::mpprintf_va(mp, iformat, newcopy);
   va_end(newcopy); va_end(args);
   kdb->AddPublicKey(kname, "RSA", GetPayload(payload_number), NULL);
}

void BrokerMessage::TakeAndInsertOwnedPublicKeyByName(KeyDB *kdb, int payload_number, const char *iowner, const char *iformat, ...)
{
   if (!kdb)
   {
      errprint("key database is null");
      return;
   }
   int total_payloads = GetHowManyPayloads();
   if ((payload_number < 0) || (payload_number >= total_payloads))
   {
      errprint("payload number out of range");
      return;
   }
   va_list args; va_list newcopy;
   va_start(args, iformat); va_copy(newcopy, args);
   const char *kname = StringUtils::mpprintf_va(mp, iformat, newcopy);
   va_end(newcopy); va_end(args);
   kdb->AddPublicKey(kname, "RSA", GetPayload(payload_number), iowner);
}

void BrokerMessage::AttachPublicKeyByName(KeyDB *kdb, const char *iformat, ...)
{
   if (!kdb)
   {
      errprint("key database is null");
      AddVerbatimPayloadString("");
      return;
   }
   va_list args; va_list newcopy;
   va_start(args, iformat); va_copy(newcopy, args);
   const char *kname = StringUtils::mpprintf_va(mp, iformat, newcopy);
   va_end(newcopy); va_end(args);
   KeyPair *apubkey = kdb->GetPublicKeyByName(kname);
   if (apubkey == NULL)
   {
      errprint("could not get public key named '%s'", kname);
      AddVerbatimPayloadString("");
      return;
   }
   AttachPublicKey(apubkey);
   delete apubkey;
}

void BrokerMessage::AttachPublicKey(KeyPair *apubkey)
{
   char *pubkey_string = apubkey->ExportPublicKey();
   AddVerbatimPayloadString(pubkey_string);
   free(pubkey_string);
}

void BrokerMessage::TakeAndInsertPrivateKeyByName(KeyDB *kdb, int payload_number, const char *iformat, ...)
{
   if (!kdb)
   {
      errprint("key database is null");
      return;
   }
   int total_payloads = GetHowManyPayloads();
   if ((payload_number < 0) || (payload_number >= total_payloads))
   {
      errprint("payload number out of range");
      return;
   }
   va_list args; va_list newcopy;
   va_start(args, iformat); va_copy(newcopy, args);
   const char *kname = StringUtils::mpprintf_va(mp, iformat, newcopy);
   va_end(newcopy); va_end(args);
   RSAKeyPair *bkp = new RSAKeyPair();
   bkp->ImportPrivateKey(GetPayload(payload_number));
   kdb->AddPrivateKeyWithPassword(kname, kdb->WIPpassword, bkp, NULL);
   delete bkp;
}

void BrokerMessage::TakeAndInsertOwnedPrivateKeyByName(KeyDB *kdb, int payload_number, const char *iowner, const char *iformat, ...)
{
   if (!kdb)
   {
      errprint("key database is null");
      return;
   }
   int total_payloads = GetHowManyPayloads();
   if ((payload_number < 0) || (payload_number >= total_payloads))
   {
      errprint("payload number out of range");
      return;
   }
   va_list args; va_list newcopy;
   va_start(args, iformat); va_copy(newcopy, args);
   const char *kname = StringUtils::mpprintf_va(mp, iformat, newcopy);
   va_end(newcopy); va_end(args);
   RSAKeyPair *bkp = new RSAKeyPair();
   bkp->ImportPrivateKey(GetPayload(payload_number));
   kdb->AddPrivateKeyWithPassword(kname, kdb->WIPpassword, bkp, iowner);
   delete bkp;
}

void BrokerMessage::AttachPrivateKeyByName(KeyDB *kdb, const char *iformat, ...)
{
   if (!kdb)
   {
      errprint("key database is null");
      AddVerbatimPayloadString("");
      return;
   }
   va_list args; va_list newcopy;
   va_start(args, iformat); va_copy(newcopy, args);
   const char *kname = StringUtils::mpprintf_va(mp, iformat, newcopy);
   va_end(newcopy); va_end(args);
   KeyPair *aprvkey = kdb->GetPrivateKeyByName(kname);
   if (aprvkey == NULL)
   {
      errprint("could not get private key named '%s'", kname);
      AddVerbatimPayloadString("");
      return;
   }
   AttachPrivateKey(aprvkey);
   delete aprvkey;
}

void BrokerMessage::AttachPrivateKey(KeyPair *aprvkey)
{
   char *prvkey_string = aprvkey->ExportPrivateKey();
   AddVerbatimPayloadString(prvkey_string);
   free(prvkey_string);
}

int BrokerMessage::TakeAndWriteOutFileByName(int payload_number, const char *iformatfname, ...)
{
   int result = 0;
   va_list args; va_list newcopy;
   va_start(args, iformatfname); va_copy(newcopy, args);
   const char *fname = StringUtils::mpprintf_va(mp, iformatfname, newcopy);
   va_end(newcopy); va_end(args);
   apr_status_t status;
   apr_file_t *newFP;
   status = apr_file_open(&newFP, StringUtils::expandMyHomeDirectoryTilde(fname, mp), APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_BUFFERED, APR_OS_DEFAULT, mp);
   if (status == 0)
   {
      apr_size_t cesize = GetPayloadLength(payload_number);
      status = apr_file_write_full(newFP, GetPayload(payload_number), cesize, &cesize);
      CheckAPRError(status);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      return (cesize == GetPayloadLength(payload_number));
   } else
   {
      CheckAPRError(status);
      errprint("problem opening file '%s'", fname);
   }
   return 0;
}

int BrokerMessage::TakeAndCWriteOutFileByName(int payload_number, const char *iformatfname, ...)
{
   int result = 0;
   va_list args; va_list newcopy;
   va_start(args, iformatfname); va_copy(newcopy, args);
   const char *fname = StringUtils::mpprintf_va(mp, iformatfname, newcopy);
   va_end(newcopy); va_end(args);
   apr_status_t status;
   apr_file_t *newFP;
   status = apr_file_open(&newFP, StringUtils::expandMyHomeDirectoryTilde(fname, mp), APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_BUFFERED, APR_OS_DEFAULT, mp);
   if (status == 0)
   {
      apr_size_t cesize = GetPayloadLength(payload_number);
      status = apr_file_write_full(newFP, GetPayload(payload_number), cesize, &cesize);
      CheckAPRError(status);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      return (cesize == GetPayloadLength(payload_number));
   } else
   {
      errprint("problem opening file '%s'", fname);
   }
   return 0;
}

int BrokerMessage::TakeAndCTWriteOutFileByName(int payload_number, const char *iformatfname, ...)
{
   int result = 0;
   va_list args; va_list newcopy;
   va_start(args, iformatfname); va_copy(newcopy, args);
   const char *fname = StringUtils::mpprintf_va(mp, iformatfname, newcopy);
   va_end(newcopy); va_end(args);
   apr_status_t status;
   apr_file_t *newFP;
   status = apr_file_open(&newFP, StringUtils::expandMyHomeDirectoryTilde(fname, mp), APR_FOPEN_TRUNCATE|APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_BUFFERED, APR_OS_DEFAULT, mp);
   if (status == 0)
   {
      apr_size_t cesize = GetPayloadLength(payload_number);
      status = apr_file_write_full(newFP, GetPayload(payload_number), cesize, &cesize);
      CheckAPRError(status);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      return (cesize == GetPayloadLength(payload_number));
   } else
   {
      CheckAPRError(status);
      errprint("problem opening file '%s'", fname);
   }
   return 0;
}

apr_size_t BrokerMessage::AttachHashThenFileByName(const char *iformatfname, ...)
{
   va_list args; va_list newcopy;
   va_start(args, iformatfname); va_copy(newcopy, args);
   const char *fname = StringUtils::mpprintf_va(mp, iformatfname, newcopy);
   va_end(newcopy); va_end(args);
   apr_status_t status;
   apr_file_t *newFP;
   status = apr_file_open(&newFP, StringUtils::expandMyHomeDirectoryTilde(fname, mp), APR_FOPEN_READ|APR_FOPEN_BINARY|APR_FOPEN_BUFFERED, APR_OS_DEFAULT, mp);
   CheckAPRError(status);
   apr_finfo_t finfo;
#ifdef USE_STAT_FILE_SIZE
   status = apr_file_info_get(&finfo, APR_FINFO_SIZE, newFP);
   CheckAPRError(status);
#else
   apr_off_t offset = 0;
   status = apr_file_seek(newFP, APR_END, &offset);
   CheckAPRError(status);
   finfo.size = offset;
   offset = 0;
   status = apr_file_seek(newFP, APR_SET, &offset);
   CheckAPRError(status);
#endif
   if (finfo.size == 0)
   {
      return 0;
   }
   apr_size_t fullread = finfo.size;
   char *encd = (char *)malloc(sizeof(char)*(finfo.size));
   status = apr_file_read_full(newFP, encd, finfo.size, &fullread);
   CheckAPRError(status);
   char *phash = BaseConvert::GetSHA1Hash(encd, fullread);
   AddVerbatimPayloadString(phash);
   free(phash);
   if (fullread != finfo.size)
   {
      errprint("expected to read %d bytes, actually read %d", finfo.size, fullread);
      AddVerbatimPayloadString("INVALID");
   } else
   {
      AddPayloadBuffer(encd, finfo.size);
   }
   free(encd);
   return finfo.size;
}

void BrokerMessage::AttachEncryptedXMLDescriptorByName(KeyDB *keydb, const char *keyname, const char *iformatfname, ...)
{
   va_list args; va_list newcopy;
   va_start(args, iformatfname); va_copy(newcopy, args);
   const char *fname = StringUtils::mpprintf_va(mp, iformatfname, newcopy);
   va_end(newcopy); va_end(args);
   XMLDescriptor *xmldescr = new XMLDescriptor();
   xmldescr->ReadEncrypted(keydb, keyname, fname, mp);
   char *result = xmldescr->WritePlain();
   AddPayloadBuffer(result, strlen(result));
   free(result);
   delete xmldescr;
}

void BrokerMessage::UpdateAndAttachXMLDescriptorByName(int *cancel, int *blocknumber, const char *cname, KeyPair *trialkey, const char *directoryname, const char *iformatfname, ...)
{
   va_list args; va_list newcopy;
   va_start(args, iformatfname); va_copy(newcopy, args);
   const char *fname = StringUtils::mpprintf_va(mp, iformatfname, newcopy);
   va_end(newcopy); va_end(args);

   XMLDescriptor *xmldescr = new XMLDescriptor();
   xmldescr->ReadPlain(fname);
   xmldescr->GenerateXMLTreeForBlockTransfer(cancel, cname, trialkey, directoryname, STANDARD_BLOCK, mp, blocknumber);
   char *resultout = xmldescr->WritePlain();
   AddPayloadBuffer(resultout, strlen(resultout));
   free(resultout);
   delete xmldescr;
}
